package net.maku.framework.security.wx;

import net.maku.framework.security.mobile.MobileAuthenticationToken;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper;
import org.springframework.security.core.authority.mapping.NullAuthoritiesMapper;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.util.Assert;

/**
 * @author Administrator
 */
public class WxUnionIdAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {

    protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
    private final GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();

    private final WxUnionIdUserDetailsService wxUnionIdUserDetailsService;

    public WxUnionIdAuthenticationProvider(WxUnionIdUserDetailsService wxUnionIdUserDetailsService) {
        this.wxUnionIdUserDetailsService = wxUnionIdUserDetailsService;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(wxUnionIdUserDetailsService, "wxUnionIdUserDetailsService must not be null");
    }

    @Override
    public void setMessageSource(MessageSource messageSource) {
        this.messages = new MessageSourceAccessor(messageSource);
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return WxUnionIdAuthenticationToken.class.isAssignableFrom(authentication);
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Assert.isInstanceOf(WxUnionIdAuthenticationToken.class, authentication,
                () -> messages.getMessage(
                        "MobileAuthenticationProvider.onlySupports",
                        "Only MobileAuthenticationProvider is supported"));

        WxUnionIdAuthenticationToken authenticationToken = (WxUnionIdAuthenticationToken) authentication;
        String unionId = authenticationToken.getName();
        String code = (String) authenticationToken.getCredentials();

        try {
            UserDetails userDetails = wxUnionIdUserDetailsService.loadUserByUnionId(unionId);
            if (userDetails == null) {
                throw new BadCredentialsException("Bad credentials");
            }
            return createSuccessAuthentication(authentication,userDetails);
        } catch (UsernameNotFoundException ex) {
            throw new BadCredentialsException(this.messages
                    .getMessage("WxUnionIdAuthenticationProvider.badCredentials", "Bad credentials"));
        }
    }

    protected Authentication createSuccessAuthentication(Authentication authentication, UserDetails user) {
        WxUnionIdAuthenticationToken result = new WxUnionIdAuthenticationToken(authoritiesMapper.mapAuthorities(user.getAuthorities()), user,
                null);
        result.setDetails(authentication.getDetails());
        return result;
    }
}
